﻿using EnvDTE;
using EnvDTE80;
using Microsoft;
using Microsoft.VisualStudio.Shell;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace FierceVSIX
{
    public class FierceGenerate
    {
        private const string SolutionName = "Fierce";
        private const string NetStandard = "netstandard2.0";

        Solution solution;
        private string path;
        private string solutionConfiguration;
        private List<FierceEntityModel> list;

        public async System.Threading.Tasks.Task ExecuteGenerateAsync(AsyncPackage package)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

            DTE dte = (DTE)await package.GetServiceAsync(typeof(DTE));
            Assumes.Present(dte);
            solution = dte.Solution;
            path = solution.FullName.Replace($"{SolutionName}.sln", string.Empty);

            Project entityProject = GetProject(solution.Projects, $"{SolutionName}.Entity");
            if (entityProject == null) { return; }
            solutionConfiguration = solution.SolutionBuild.ActiveConfiguration.Name;
            solution.SolutionBuild.BuildProject(solutionConfiguration, entityProject.FullName, true);

            TypeInit();

            try
            {
                T4Configuration();
                T4DbSet();
                T4DataContext();
                T4Service();
                T4ServiceFactory();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally { }
        }

        private Project GetProject(Projects ps, string name)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            foreach (Project p in ps)
            {
                if (p.Kind.Equals(ProjectKinds.vsProjectKindSolutionFolder))
                {
                    var pp = GetProject(p.ProjectItems, name);
                    if (pp != null) { return pp; }
                }
                else { if (p.Name.Equals(name)) { return p; } }
            }

            return null;
        }

        private Project GetProject(ProjectItems ps, string name)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            foreach (ProjectItem pi in ps)
            {
                Project p = pi.SubProject;

                if (p.Kind.Equals(ProjectKinds.vsProjectKindSolutionFolder))
                {
                    var pp = GetProject(p.ProjectItems, name);
                    if (pp != null) { return pp; }
                }
                else { if (p.Name.Equals(name)) { return p; } }
            }

            return null;
        }

        private string GetAreaName(string typeNamespace)
        {
            return Regex.Match(typeNamespace, $"{SolutionName}.Entity.(?<area>.*)").Groups["area"].Value;
        }

        private void TypeInit()
        {
            string modelFile = $@"{path}{SolutionName}.Entity\bin\{solutionConfiguration}\{NetStandard}\{SolutionName}.Entity.dll";

            byte[] fileData = File.ReadAllBytes(modelFile);

            var types = Assembly.Load(fileData).GetTypes().Where(m => !m.IsAbstract);

            list = new List<FierceEntityModel>();

            foreach (Type type in types)
            {
                string areaName = GetAreaName(type.Namespace);
                string typeName = type.BaseType.Name.Replace("Entity", string.Empty);
                list.Add(new FierceEntityModel(areaName, typeName, type.Name, type));
            }
        }

        public static void ClassFileWrite(string path, string file, string text)
        {
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }

            File.WriteAllText($"{path}{file}", text);
        }

        private void T4Configuration()
        {
            foreach (var item in list)
            {
                string t4Path = $@"{path}{SolutionName}.Core\Configuration\{item.AreaName}\Generate\";
                T4.Configuration t4 = new T4.Configuration(SolutionName, item.AreaName, item.EntityName);
                ClassFileWrite(t4Path, $"{item.EntityName}Configuration.cs", t4.TransformText());
            }
        }

        private void T4DbSet()
        {
            foreach (var item in list)
            {
                string t4Path = $@"{path}{SolutionName}.Core\DbSet\{item.AreaName}\Generate\";
                T4.DbSet t4 = new T4.DbSet(SolutionName, item.AreaName, item.EntityName, item.TypeName);
                ClassFileWrite(t4Path, $"{item.EntityName}DbSet.cs", t4.TransformText());
            }
        }

        private void T4DataContext()
        {
            string t4Path = $@"{path}{SolutionName}.Core\Context\";
            T4.DataContext t4 = new T4.DataContext(SolutionName, list);
            ClassFileWrite(t4Path, "DataContext.Generate.cs", t4.TransformText());
        }

        private void T4Service()
        {
            foreach (var item in list)
            {
                string t4Path = $@"{path}{SolutionName}.Core\Service\{item.AreaName}\Generate\";
                T4.Service t4 = new T4.Service(SolutionName, item.AreaName, item.EntityName, item.TypeName);
                ClassFileWrite(t4Path, $"{item.EntityName}Service.cs", t4.TransformText());
            }
        }

        private void T4ServiceFactory()
        {
            string t4Path = $@"{path}{SolutionName}.Core\Aop\";
            T4.ServiceFactory t4 = new T4.ServiceFactory(SolutionName, list);
            ClassFileWrite(t4Path, "ServiceFactory.Generate.cs", t4.TransformText());
        }
    }
}